Skip to content

Fix Resume() to fully reinitialize USB device after suspend#249

Open
fcatuhe wants to merge 1 commit into
uunicorn:masterfrom
fcatuhe:fix/survive-usb-errors
Open

Fix Resume() to fully reinitialize USB device after suspend#249
fcatuhe wants to merge 1 commit into
uunicorn:masterfrom
fcatuhe:fix/survive-usb-errors

Conversation

@fcatuhe
Copy link
Copy Markdown

@fcatuhe fcatuhe commented Mar 17, 2026

Replace the broken Resume() implementation with a proper retry loop that does a full init.open() (USB re-enumeration + sensor init), giving the kernel time to enumerate the USB device after wake.

Motivation

The existing Resume() called init.open_common() which skips usb.open() — it assumes the USB handle is still valid. After suspend, the kernel resets the USB bus and the handle goes stale, causing USBError: [Errno 19] No such device. The original single bare-except retry was also insufficient since the USB device may not be enumerated yet when Resume() is called.

This became visible after open-fprintd PR #24 added PrepareForSleep handling, which now calls Device.Resume() automatically on wake. The hyprlock maintainer rightly pointed out that DBus services should handle suspend internally and stay alive across sleep cycles.

Related issues

This change, together with open-fprintd PR #24, should address:

Related PRs

Changes

Changes to dbus_service/dbus-service and validitysensor/usb.py:

  • Call init.open() instead of init.open_common() to get a fresh USB handle
  • Retry up to 20 times with 100ms delay (2s max) for USB enumeration
  • Change open_dev() to raise USBError instead of a bare Exception when the device is not found, so the retry loop can catch USBError specifically

Testing

Tested on a ThinkPad X1 Carbon 6th Gen (06cb:009a Synaptics reader) running Arch Linux (Omarchy) with open-fprintd + python-validity. Multiple suspend/resume cycles — USB device typically enumerates after 0-2 failed attempts (~0-200ms). Fingerprint verification works reliably after wake. Tested both successful matches and no-match retries across multiple cycles without service crashes.

Replace the broken Resume() with a retry loop that does a full
init.open() (USB re-enumeration + sensor init), giving the kernel
time to enumerate the USB device after wake.

- Call init.open() instead of init.open_common() for fresh USB handle
- Retry up to 20 times with 100ms delay (2s max) for USB enumeration
- Catch Exception since usb.open() raises plain Exception when device
  is not yet enumerated
@NhProGamer
Copy link
Copy Markdown

I tested this on my T480, it seems to fix issues related, good job

@PedroBizachi
Copy link
Copy Markdown

Test report: suspend/resume fix works on ThinkPad T480 (06cb:009a)

I tested the combined open-fprintd + python-validity suspend/resume fixes on my machine and can confirm that the solution works here unlike any other solution already provided.

Hardware / system

  • Machine: Lenovo ThinkPad T480
  • Firmware: N24ET81W (1.56)
  • Fingerprint reader: 06cb:009a Synaptics, Inc. Metallica MIS Touch Fingerprint Reader
  • OS: Arch Linux / Omarchy
  • Kernel: 7.0.9-arch2-1
  • Suspend mode: deep (/sys/power/mem_sleep shows s2idle [deep])

Packages / commits tested

  • open-fprintd: 0.7.r3.g28b6d9b-1
  • open-fprintd commit: 28b6d9b Handle suspend/resume internally via logind PrepareForSleep
  • python-validity: 0.15.r1.g3d52182-1
  • python-validity commit: 3d52182 Fix Resume() to fully reinitialize USB device after suspend
  • fprintd-clients-git: 1.90.1.r2.g54e56d6-10
  • python3-validity-suspend-hotfix.service: not used / disabled

What failed before

With only the open-fprintd PrepareForSleep change installed, but with python-validity still on 0.15 / master, the device worked before suspend but failed after resume.

The failure matched the stale USB handle issue described in the PR:

PrepareForSleep: waking up
python3-validity: In Resume
usb.core.USBError: [Errno 19] No such device (it may have been disconnected)

This happened because the old Resume() implementation called init.open_common(), so it reused the old USB handle after the kernel reset/re-enumerated the USB bus.

What works after applying both fixes

After switching python-validity to fix/survive-usb-errors and rebuilding/installing it, suspend/resume works.

Relevant log from a successful test cycle:

open-fprintd: DEBUG:root:Registered PrepareForSleep listener on logind
open-fprintd: DEBUG:root:PrepareForSleep: going to sleep
python3-validity: DEBUG:root:In Suspend
kernel: PM: suspend entry (deep)
kernel: xhci_hcd 0000:3c:00.0: xHC error in resume, USBSTS 0x401, Reinit
systemd-sleep: System returned from sleep operation 'suspend'.
open-fprintd: DEBUG:root:PrepareForSleep: waking up
python3-validity: DEBUG:root:In Resume
python3-validity: DEBUG:root:Resume attempt 1 failed: USBError(None, 'No matching devices found')
open-fprintd: DEBUG:root:VerifyStart
open-fprintd: DEBUG:root:VerifyFingerSelected
open-fprintd: DEBUG:root:VerifyStatus
open-fprintd: DEBUG:root:VerifyStop

The important part is that the first resume attempt failed while the USB device was still being enumerated, but the retry loop recovered and fingerprint verification worked immediately after wake.

Test steps performed

fprintd-verify
systemctl suspend
fprintd-verify

I also enrolled and verified a fingerprint successfully before the suspend/resume test.

Result

Confirmed working on Lenovo ThinkPad T480 with 06cb:009a Synaptics reader on Arch Linux / Omarchy. @fcatuhe @uunicorn

The fix appears to require both sides:

  • open-fprintd handling PrepareForSleep
  • python-validity using full init.open() with retry in Resume()

Using the old python-validity Resume() implementation still fails with USBError: [Errno 19] No such device after resume.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants